/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.repair.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.repair.dto.statistical.WorkGroupStatisticalDTO;
import org.springblade.modules.repair.dto.statistical.WorkStatisticalDTO;
import org.springblade.modules.repair.entity.MaintainPlanScoreRecord;
import org.springblade.modules.repair.mapper.MaintainPlanScoreRecordMapper;
import org.springblade.modules.repair.service.IMaintainPlanScoreRecordService;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.repair.vo.statistics.OrderScoreCompareCharVO;
import org.springblade.modules.repair.vo.statistics.OrderScoreDetailCharVO;
import org.springblade.modules.repair.vo.statistics.OrderScorePieCharVO;
import org.springblade.modules.repair.vo.statistics.ScoreItemVO;
import org.springblade.modules.system.entity.User;
import org.springblade.modules.system.service.IUserService;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 任务打分记录表 服务实现类
 *
 * @author ysq
 * @since 2022-09-22
 */
@Service
@AllArgsConstructor
public class MaintainPlanScoreRecordServiceImpl extends BaseServiceImpl<MaintainPlanScoreRecordMapper, MaintainPlanScoreRecord> implements IMaintainPlanScoreRecordService {

	private final IUserService userService;

	private List<MaintainPlanScoreRecord> scoreRecordList(List<Long> userIds, Date beginDate, Date endDate) {
		List<MaintainPlanScoreRecord> scoreRecordList = baseMapper.selectList(Wrappers.<MaintainPlanScoreRecord>lambdaQuery()
			.in(MaintainPlanScoreRecord :: getUserId, userIds)
			.between(MaintainPlanScoreRecord :: getCreateTime, beginDate, DateUtil.endOfDay(endDate)));
		return scoreRecordList;
	}

	@Override
	public OrderScorePieCharVO orderScorePieChar(WorkStatisticalDTO params) {
		List<MaintainPlanScoreRecord> scoreRecordList = this.scoreRecordList(params.getUserIds(), params.getBeginDate(), params.getEndDate());
		// 根据planId去重
		scoreRecordList = scoreRecordList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(t -> t.getPlanId()))), ArrayList :: new));
		// 工单总数
		long orderTotal = scoreRecordList.stream().count();
		// 按档次分组
		Map<Integer, List<MaintainPlanScoreRecord>> scoreLevelMap = this.getScoreByLevel(scoreRecordList);
		OrderScorePieCharVO orderScorePieCharVO = new OrderScorePieCharVO();
		orderScorePieCharVO.setOrderTotal(orderTotal);
		scoreLevelMap.forEach((k, v) -> {
			long orderCount = v.size();
			// 占比
			BigDecimal ratio = new BigDecimal(orderCount).divide(new BigDecimal(orderTotal), 3, BigDecimal.ROUND_HALF_UP);
			switch (k) {
				case 1: {
					orderScorePieCharVO.setValue1(ratio);
					break;
				}
				case 2: {
					orderScorePieCharVO.setValue2(ratio);
					break;
				}
				case 3: {
					orderScorePieCharVO.setValue3(ratio);
					break;
				}
				case 4: {
					orderScorePieCharVO.setValue4(ratio);
					break;
				}
				default:
			}
		});

		return orderScorePieCharVO;
	}

	@Override
	public OrderScoreCompareCharVO orderScoreCompareDeptChar(WorkGroupStatisticalDTO params) {
		OrderScoreCompareCharVO orderScoreCompareCharVO = new OrderScoreCompareCharVO();
		List<Long> allUserIds = new ArrayList<>();
		params.getDeptGroups().forEach(t -> allUserIds.addAll(t.getUserIds()));
		// 评分工单总数
		long orderTotal = this.scoreRecordList(allUserIds, params.getBeginDate(), params.getEndDate()).stream().map(t -> t.getPlanId()).distinct().count();
		orderScoreCompareCharVO.setOrderTotal(orderTotal);


		params.getDeptGroups().forEach(deptGroup -> {
			ScoreItemVO scoreItemVO = new ScoreItemVO();
			scoreItemVO.setName(deptGroup.getDeptName());
			if (orderTotal > 0) {
				// 组评分工单数
				long deptOrderCount = this.scoreRecordList(deptGroup.getUserIds(), params.getBeginDate(), params.getEndDate()).stream().map(t -> t.getPlanId()).distinct().count();
				scoreItemVO.setValue1(deptOrderCount);
				scoreItemVO.setValue2(new BigDecimal(deptOrderCount).divide(new BigDecimal(orderTotal), 3, BigDecimal.ROUND_HALF_UP));
			}
			orderScoreCompareCharVO.getScoreItems().add(scoreItemVO);
		});

		return orderScoreCompareCharVO;
	}

	@Override
	public OrderScoreCompareCharVO orderScoreCompareUserChar(WorkStatisticalDTO params) {
		OrderScoreCompareCharVO orderScoreCompareCharVO = new OrderScoreCompareCharVO();
		// 评分工单总数
		long orderTotal = this.scoreRecordList(params.getUserIds(), params.getBeginDate(), params.getEndDate()).stream().map(t -> t.getPlanId()).distinct().count();
		orderScoreCompareCharVO.setOrderTotal(orderTotal);

		List<User> userList = userService.listByIds(params.getUserIds());
		Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(t -> t.getId(), t -> t.getRealName()));
		params.getUserIds().forEach(userId -> {
			ScoreItemVO scoreItemVO = new ScoreItemVO();
			scoreItemVO.setName(userMap.get(userId));
			// 个人评分工单数
			long userOrderCount = this.scoreRecordList(Arrays.asList(userId), params.getBeginDate(), params.getEndDate()).size();
			scoreItemVO.setValue1(userOrderCount);
			if (orderTotal > 0) {
				scoreItemVO.setValue2(new BigDecimal(userOrderCount).divide(new BigDecimal(orderTotal), 3, BigDecimal.ROUND_HALF_UP));
			}
			orderScoreCompareCharVO.getScoreItems().add(scoreItemVO);
		});

		return orderScoreCompareCharVO;
	}

	@Override
	public List<OrderScoreDetailCharVO> orderScoreDetailDeptChar(WorkGroupStatisticalDTO params) {
		List<OrderScoreDetailCharVO> detailCharVOList = new ArrayList<>();
		params.getDeptGroups().forEach(deptGroup -> {
			OrderScoreDetailCharVO orderScoreDetailCharVO = new OrderScoreDetailCharVO();
			orderScoreDetailCharVO.setName(deptGroup.getDeptName());
			List<MaintainPlanScoreRecord> scoreRecordList = this.scoreRecordList(deptGroup.getUserIds(), params.getBeginDate(), params.getEndDate());
			// 根据planId去重
			scoreRecordList = scoreRecordList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(t -> t.getPlanId()))), ArrayList :: new));
			if (Func.isNotEmpty(scoreRecordList)) {
				// 总分
				BigDecimal sumScore = scoreRecordList.stream().collect(Collectors.reducing(BigDecimal.ZERO, t -> t.getScore(), BigDecimal :: add));
				// 平均分
				orderScoreDetailCharVO.setDivScore(sumScore.divide(new BigDecimal(scoreRecordList.size()), 1, BigDecimal.ROUND_HALF_UP));
				// 根据分数档次分组
				Map<Integer, List<MaintainPlanScoreRecord>> scoreLevelMap = this.getScoreByLevel(scoreRecordList);
				scoreLevelMap.forEach((k, v) -> {
					long orderCount = v.size();
					switch (k) {
						case 1: {
							orderScoreDetailCharVO.setValue1(orderCount);
							break;
						}
						case 2: {
							orderScoreDetailCharVO.setValue2(orderCount);
							break;
						}
						case 3: {
							orderScoreDetailCharVO.setValue3(orderCount);
							break;
						}
						case 4: {
							orderScoreDetailCharVO.setValue4(orderCount);
							break;
						}
						default:
					}
				});
			}
			detailCharVOList.add(orderScoreDetailCharVO);
		});

		return detailCharVOList;
	}

	@Override
	public List<OrderScoreDetailCharVO> orderScoreDetailUserChar(WorkStatisticalDTO params) {
		List<OrderScoreDetailCharVO> detailCharVOList = new ArrayList<>();
		List<User> userList = userService.listByIds(params.getUserIds());
		Map<Long, String> userMap = userList.stream().collect(Collectors.toMap(t -> t.getId(), t -> t.getRealName()));
		params.getUserIds().forEach(userId -> {
			OrderScoreDetailCharVO orderScoreDetailCharVO = new OrderScoreDetailCharVO();
			orderScoreDetailCharVO.setName(userMap.get(userId));
			List<MaintainPlanScoreRecord> scoreRecordList = this.scoreRecordList(Arrays.asList(userId), params.getBeginDate(), params.getEndDate());
			if (Func.isNotEmpty(scoreRecordList)) {
				// 总分
				BigDecimal sumScore = scoreRecordList.stream().collect(Collectors.reducing(BigDecimal.ZERO, t -> t.getScore(), BigDecimal :: add));
				// 平均分
				orderScoreDetailCharVO.setDivScore(sumScore.divide(new BigDecimal(scoreRecordList.size()), 1, BigDecimal.ROUND_HALF_UP));
				// 根据分数档次分组
				Map<Integer, List<MaintainPlanScoreRecord>> scoreLevelMap = this.getScoreByLevel(scoreRecordList);
				scoreLevelMap.forEach((k, v) -> {
					long orderCount = v.size();
					switch (k) {
						case 1: {
							orderScoreDetailCharVO.setValue1(orderCount);
							break;
						}
						case 2: {
							orderScoreDetailCharVO.setValue2(orderCount);
							break;
						}
						case 3: {
							orderScoreDetailCharVO.setValue3(orderCount);
							break;
						}
						case 4: {
							orderScoreDetailCharVO.setValue4(orderCount);
							break;
						}
						default:
					}
				});
			}
			detailCharVOList.add(orderScoreDetailCharVO);
		});

		return detailCharVOList;
	}

	/**
	 * 根据分数档次分组
	 * @param data
	 * @return
	 */
	private Map<Integer, List<MaintainPlanScoreRecord>> getScoreByLevel(List<MaintainPlanScoreRecord> data) {
		// 按档次分组
		Map<Integer, List<MaintainPlanScoreRecord>> scoreLevelMap = data.stream().collect(Collectors.groupingBy(scoreRecord -> {
			double score = scoreRecord.getScore().doubleValue();
			if (score >= 0 && score <= 60) {
				return 1;
			}else if (score > 60 && score <= 80) {
				return 2;
			}else if (score > 80 && score <= 100) {
				return 3;
			} else {
				return 4;
			}
		}));

		return scoreLevelMap;
	}
}
